use std::any::Any;
use std::error::Error;
use std::fs::read_to_string;
use std::io::BufReader;

use anyhow::anyhow;
use log::{error, info};
use schemars::_private::NoSerialize;
use schemars::schema::RootSchema;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::json;

use crate::config::load_from_yaml;
use crate::config::props::log_config_prop::{Appender, Kind};
// 根配置
#[derive(Debug, Deserialize, Serialize)]
pub struct RootAppConfig {
    pub actix: Option<ActixConf>,
    pub sqlite: Option<SqliteConf>,
    pub mysql: Option<MysqlConf>,
    pub log: Option<Vec<Log>>,
}

impl RootAppConfig {
    pub fn new(file_path: &str) -> Self {
        load_from_yaml::<RootAppConfig>(file_path).expect("check your root yml file")
    }
}

// actix配置
#[derive(Debug, Deserialize, Serialize)]
pub struct ActixConf {
    pub server: Option<String>,
    pub port: Option<u16>,
}
// sqlite配置
#[derive(Debug, Deserialize, Serialize)]
pub struct SqliteConf {
    pub url: Option<String>,
}
// mysql配置
#[derive(Debug, Deserialize, Serialize)]
pub struct MysqlConf {
    pub host: Option<String>,
    pub port: Option<u16>,
    pub username: Option<String>,
    pub password: Option<String>,
    pub database: Option<String>,
}
// log配置
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Log {
    pub appender: Option<Appender>,
}
mod log_config_prop {
    use ftlog::appender::Duration;
    use ftlog::appender::Period;
    use log::LevelFilter::Info;
    use log::{Level, LevelFilter};

    const PERIODS: [(&str, Period); 5] = [
        ("Minute", Period::Minute),
        ("Hour", Period::Hour),
        ("Day", Period::Day),
        ("Month", Period::Month),
        ("Year", Period::Year),
    ];

    const LEVELS: [(&str, LevelFilter); 6] = [
        ("Off", LevelFilter::Off),
        ("Error", LevelFilter::Error),
        ("Warn", LevelFilter::Warn),
        ("Info", LevelFilter::Info),
        ("Debug", LevelFilter::Debug),
        ("Trace", LevelFilter::Trace),
    ];

    const DURATIONS: [(&str, fn(i64) -> Duration); 5] = [
        ("W", Duration::weeks),
        ("D", Duration::days),
        ("H", Duration::hours),
        ("M", Duration::minutes),
        ("S", Duration::seconds),
    ];

    #[derive(Debug, serde::Deserialize, serde::Serialize)]
    pub struct File {
        pub path: Option<String>,
        pub period: Option<String>,
        pub duration: Option<String>,
    }

    impl File {
        pub fn period(&self) -> Period {
            match &self.period {
                None => Period::Day,
                Some(period) => {
                    for x in PERIODS {
                        if x.0 == period.as_str() {
                            return x.1;
                        }
                    }
                    Period::Day
                }
            }
        }
        pub fn duration(&self) -> Duration {
            match &self.duration {
                None => Duration::weeks(1),
                Some(duration) => {
                    for x in DURATIONS {
                        if let Some(index) = duration.rfind(x.0) {
                            return x.1(duration[0..index].parse::<i64>().unwrap());
                        };
                    }
                    Duration::weeks(1)
                }
            }
        }
        pub fn path(&self) -> &str {
            match &self.path {
                None => panic!("check log.appender.kind.file.path!"),
                Some(path) => path.as_str(),
            }
        }
    }
    // #[derive(Debug, serde::Deserialize, serde::Serialize)]
    // pub struct Console {
    //     pub path: Option<bool>,
    // }

    #[derive(Debug, serde::Deserialize, serde::Serialize)]
    pub struct Kind {
        pub file: Option<File>,
        pub console: Option<bool>,
    }

    #[derive(Debug, serde::Deserialize, serde::Serialize)]
    pub struct Appender {
        pub module: Option<String>,
        pub level: Option<String>,
        pub kind: Option<Kind>,
    }

    impl Appender {
        pub fn level(&self) -> LevelFilter {
            self.level.as_ref().map_or(Info, |level| {
                (*LEVELS.iter().rfind(|e| (**e).0 == level.as_str()).unwrap()).1
            })
        }
    }
}

#[test]
fn test() {
    let config = load_from_yaml::<RootAppConfig>("src/app.yml");
    println!("{:?}", config);
}
